import mido
from mido import MidiFile
import time
import math
import sys, getopt

import sys
sys.path.append('../../src')
sys.path.append('../..')


FILE_DEFAULT = "../../source/midi/myMidi/midiShow/千本樱-钢琴完整版.mid"
# FILE_DEFAULT = "../../source/midi/myMidi/midiShow/月半小夜曲2.mid"

# FILE_DEFAULT = "../../source/midi/myMidi/midiShow/钟.mid"

FILE_DEFAULT = "../../source/midi/周杰伦/蒲公英的约定.mid"

FILE_DEFAULT = "../../source/midi/myMidi/midiShow/父亲写的散文诗.mid"

FILE_DEFAULT = "../../source/midi/myMidi/pianoClassic/野蜂飞舞.mid"
# FILE_DEFAULT = "../../source/midi/myMidi/pianoClassic/后来2.mid"

# FILE_DEFAULT = "../../source/midi/myMidi/popularMusic/孤勇者.mid"
# FILE_DEFAULT = "../../source/midi/myMidi/popularMusic/后来2.mid"
# FILE_DEFAULT = "../../source/midi/myMidi/popularMusic/七里香.mid"

# FILE_DEFAULT = "../../source/midi/myMidi/popularMusic/爱情转移2.mid"

SAME_NOTE_INTERVAL = 0.04
TEMPO_GAIN = 1
NOTE_VELOCITY_ADJUST = 1

PROGRAM_LIST = [0, 1, 24]
VELOCITY_SCALE = {"min":100, "max":110}
NOTE_MOVING = 0
BLACK_KEY_COEFFIENT = 1

TONE_MOVING_TABLE = {"A":-1, "B":-3, "Eb":3}
class midi_parser():
    def __init__(self, file = None, pedal = True):
        self.file = file
        self.piano_play_list = []
        self.piano_velocity = {"max":0, "min":100}
        self.piano_note_scale = {"max":0, "min":127}
        self.piano_note_sta = {}
        for i in range(128):
            self.piano_note_sta.update({i:[0, 0]})

        self.change_tmpo_list = []
        self.drumkit_play_list = []

        self.current_program = 0
    def parse(self, file = None):
        if file != None:
            self.file = file

        self.mid = MidiFile(self.file)

        cur_tempo = 500000
        for i, track in enumerate(self.mid.tracks):
            print('Track {}: {}'.format(i, track.name))
            passed_time = 0
            set_tempo_idx = 0
            for i in range(128):
                self.piano_note_sta.update({i:[0, 0]})

            for msg in track:
                # print(msg)
                ab_time = mido.tick2second(msg.time, self.mid.ticks_per_beat, cur_tempo)
                if set_tempo_idx < len(self.change_tmpo_list) and  (passed_time + ab_time >= self.change_tmpo_list[set_tempo_idx][1]):
                    cur_tempo = self.change_tmpo_list[set_tempo_idx][0]
                    set_tempo_idx += 1
                    ab_time = mido.tick2second(msg.time, self.mid.ticks_per_beat, cur_tempo)

                ab_time = mido.tick2second(msg.time, self.mid.ticks_per_beat, cur_tempo)
                real_time = ab_time + passed_time
                passed_time += ab_time

                if msg.type == "program_change":
                    print(msg)
                    self.current_program = msg.program
                elif msg.type == "set_tempo":
                    print(msg)
                    self.change_tmpo_list.append([msg.tempo * TEMPO_GAIN, passed_time])
                elif msg.type == "key_signature":
                    print(msg)
                    self._cal_tone_moving(msg.key)

                if type(msg) is mido.midifiles.meta.MetaMessage:
                    pass
                    # print(msg)
                else:
                    ff = self._parse_piano(msg, real_time)
                    passed_time += ff
                    self._parse_drumKit(msg, real_time)

        print("maxVelocity:{} min:{}".format(self.piano_velocity["max"],self.piano_velocity["min"]))
        print("maxNote:{} min:{}".format(self.piano_note_scale["max"],self.piano_note_scale["min"]))

    def _parse_piano(self, msg, real_time):
        offset_time = 0
        try:
            if self.current_program in PROGRAM_LIST and msg.channel != 9:

                if msg.type == "note_on":
                        if msg.velocity == 0:
                            self.piano_play_list.append([msg.note + NOTE_MOVING, 0, real_time, 0])
                        else:
                            self.piano_play_list.append([msg.note + NOTE_MOVING, 1, real_time, msg.velocity])
                            self._statistical(msg)

                elif msg.type == "note_off":
                        self.piano_play_list.append([msg.note + NOTE_MOVING, 0, real_time, 0])

                return offset_time
            else:
                return 0

        except:
            print("piano parse error:", msg)
            # raise(e)
            return 0

    def _parse_drumKit(self, msg, real_time):
        try:
            if msg.channel == 9:
                # print("drumKit:", msg)
                if msg.type == "note_on":
                    if msg.velocity == 0:
                        self.drumkit_play_list.append([msg.note, 0, real_time, 0])
                    else:
                        self.drumkit_play_list.append([msg.note, 1, real_time, msg.velocity])
                elif msg.type == "note_off":
                    self.drumkit_play_list.append([msg.note, 0, real_time, 0])
        except:
            pass

    def _parse_gita(self):
        pass

    def _parse_others(self):
        pass

    def _cal_tone_moving(self, key_signature):
        global NOTE_MOVING
        # NOTE_MOVING = TONE_MOVING_TABLE[key_signature]
        return 0

    def _cal_speed(self, v, n = 60):
        if self.piano_velocity["max"] - self.piano_velocity["min"] == 0:
            ret = VELOCITY_SCALE["min"]
        else:
            ret = VELOCITY_SCALE["min"] + (v - self.piano_velocity["min"]) * ((VELOCITY_SCALE["max"] - VELOCITY_SCALE["min"]) / (self.piano_velocity["max"] - self.piano_velocity["min"]))

        if note.midi_table[n] < 48:
            ret *= (((1 - NOTE_VELOCITY_ADJUST) / 48) * note.midi_table[n] + NOTE_VELOCITY_ADJUST)

        if (note.midi_table[n] % 12) in [1,3,6,8,10]:
            ret *= BLACK_KEY_COEFFIENT

        return ret if ret < 100 else 100

    def _statistical(self, msg):
        if self.piano_velocity["min"] > msg.velocity:
            self.piano_velocity["min"] = msg.velocity
        if self.piano_velocity["max"] < msg.velocity:
            self.piano_velocity["max"] = msg.velocity

        if self.piano_note_scale["min"] > msg.note:
            self.piano_note_scale["min"] = msg.note
        if self.piano_note_scale["max"] < msg.note:
            self.piano_note_scale["max"] = msg.note


    def _sort_by_time(self, play_list):
        _play_list = play_list.copy()
        ret_list = []
        while _play_list != []:
            current_index = 0
            min_t = 100000
            for i in range(len(_play_list)):
                if _play_list[i][2] < min_t:
                    min_t = _play_list[i][2]
                    current_index = i
            ret_list.append(_play_list[current_index])
            del _play_list[current_index]

        return ret_list

    def play_list_sort(self):
        temp_list1 = self._sort_by_time(self.piano_play_list)

        for i in range(len(temp_list1)):
            if temp_list1[i][0] == "pedal":
                continue
            no = temp_list1[i][0]
            real_time = temp_list1[i][2]

            if temp_list1[i][1]: # on
                if not self.piano_note_sta[no][0]:
                   if real_time - self.piano_note_sta[no][1] < SAME_NOTE_INTERVAL:
                        offset_time = SAME_NOTE_INTERVAL - (real_time - self.piano_note_sta[no][1])
                        temp_list1[i][2] += offset_time
                        temp_list1[i][3] = VELOCITY_SCALE["max"]

                        for j in range(i + 1, len(temp_list1)):
                            # if j - i - 1> 400:
                            #     break
                            temp_list1[j][2] += offset_time

                self.piano_note_sta.update({no:[1, temp_list1[i][2]]})
            else:
                if self.piano_note_sta[no][0]:
                   if real_time - self.piano_note_sta[no][1] < SAME_NOTE_INTERVAL:
                        offset_time = SAME_NOTE_INTERVAL - (real_time - self.piano_note_sta[no][1])
                        temp_list1[i][2] += offset_time
                        temp_list1[i][3] = VELOCITY_SCALE["max"]

                        for j in range(i + 1, len(temp_list1)):
                            # if j - i - 1> 400:
                            #     break
                            temp_list1[j][2] += offset_time

                self.piano_note_sta.update({no:[0, temp_list1[i][2]]})


        self.piano_play_list = temp_list1


def get_play_list(file):
    _midi = midi_parser() 
    _midi.parse(file)
    _midi.play_list_sort()
    return _midi.piano_play_list
